package com.hmall.pay.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.utils.RabbitMQHelper;
import com.hmall.common.utils.UserContext;
import com.hmall.pay.constants.MQConstants;
import com.hmall.pay.domain.dto.OrderFormDTO;
import com.hmall.pay.domain.po.Order;
import com.hmall.pay.domain.po.OrderDetail;
import com.hmall.pay.mapper.OrderMapper;
import com.hmall.pay.service.IOrderDetailService;
import com.hmall.pay.service.IOrderService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2023-05-05
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    private final ItemClient itemClient;
    private final IOrderDetailService detailService;
    private final IOrderDetailService orderDetailService;
    private final RabbitMQHelper rabbitMQHelper;


    @Override
    @GlobalTransactional
    public Long createOrder(OrderFormDTO orderFormDTO) {
        // 1.订单数据
        Order order = new Order();
        // 1.1.查询商品
        List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();
        // 1.2.获取商品id和数量的Map
        Map<Long, Integer> itemNumMap = detailDTOS.stream()
                .collect(Collectors.toMap(OrderDetailDTO::getItemId, OrderDetailDTO::getNum));
        Set<Long> itemIds = itemNumMap.keySet();
        // 1.3.查询商品
        List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
        if (items == null || items.size() < itemIds.size()) {
            throw new BadRequestException("商品不存在");
        }
        // 1.4.基于商品价格、购买数量计算商品总价：totalFee
        int total = 0;
        for (ItemDTO item : items) {
            total += item.getPrice() * itemNumMap.get(item.getId());
        }
        order.setTotalFee(total);
        // 1.5.其它属性
        order.setPaymentType(orderFormDTO.getPaymentType());
        order.setUserId(UserContext.getUser());
        order.setStatus(1);
        // 1.6.将Order写入数据库order表中
        save(order);

        // 2.保存订单详情
        List<OrderDetail> details = buildDetails(order.getId(), items, itemNumMap);
        detailService.saveBatch(details);

        // 3.清理购物车商品
        try {
            rabbitMQHelper.sendWithUser(MQConstants.DELAY_EXCHANGE_NAME, MQConstants.DELAY_ORDER_KEY, itemIds);
        } catch (AmqpException e) {
            log.error("创建订单后清空购物车失败，商品id：{}", itemIds, e);
        }

        // 4.扣减库存
        try {
            itemClient.deductStock(detailDTOS);
        } catch (Exception e) {
            throw new RuntimeException("库存不足！");
        }

        // 发送延时消息给MQ，检测订单支付状态
        rabbitMQHelper.sendDelayMessage(MQConstants.DELAY_EXCHANGE_NAME, MQConstants.DELAY_ORDER_KEY, order.getId(), 30 * 60 * 1000);

        return order.getId();
    }

    @Override
    @Transactional
    public void markOrderPaySuccess(Long orderId) {
        // update order set status = 2, pay_time = now() where id = orderId and status = 1
        // 乐观锁，让更新订单状态的业务具有幂等性
        lambdaUpdate()
                .set(Order::getStatus, 2)
                .set(Order::getPayTime, LocalDateTime.now())
                .eq(Order::getId, orderId)
                .eq(Order::getStatus, 1)  // 乐观锁，只有订单状态为1(未支付)时才更新
                .update();
    }

    private List<OrderDetail> buildDetails(Long orderId, List<ItemDTO> items, Map<Long, Integer> numMap) {
        List<OrderDetail> details = new ArrayList<>(items.size());
        for (ItemDTO item : items) {
            OrderDetail detail = new OrderDetail();
            detail.setName(item.getName());
            detail.setSpec(item.getSpec());
            detail.setPrice(item.getPrice());
            detail.setNum(numMap.get(item.getId()));
            detail.setItemId(item.getId());
            detail.setImage(item.getImage());
            detail.setOrderId(orderId);
            details.add(detail);
        }
        return details;
    }

    /**
     * 取消订单：1.修改订单状态为已取消  2.恢复库存
     * @param orderId
     */
    public void cancelOrder(Long orderId) {
        // update order set status = 5, close_time = now() where id = orderId and status != 5
        lambdaUpdate()
                .set(Order::getStatus, 5)
                .set(Order::getCloseTime, LocalDateTime.now())
                .eq(Order::getId, orderId)
                .ne(Order::getStatus, 5)    // 乐观锁，使其具有幂等性
                .update();
        // 订单包含的商品信息保存在order_detail表
        // 1.查询出该订单的所有订单明细
        List<OrderDetail> orderDetails = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, orderId)
                .list();
        // 2.提取出订单明细中的商品id和数量，更新商品库存deductStock用的是批量更新，传入List<OrderDetailDTO>
        List<OrderDetailDTO> detailDTOS = BeanUtil.copyToList(orderDetails, OrderDetailDTO.class);
        // 3.批量更新商品库存
        itemClient.deductStock(detailDTOS.stream()
                .map(dto -> {
                    OrderDetailDTO reverseDto = new OrderDetailDTO();
                    reverseDto.setItemId(dto.getItemId());
                    reverseDto.setNum(-dto.getNum()); // 负数表示恢复库存
                    return reverseDto;
                })
                .collect(Collectors.toList()));
    }
}
